//
// tcccrt.c
//
// ZHI runtime library
//
// Parts of this code are (c) 2002 Fabrice Bellard 
//
// Copyright (C) 1987, 1988, 1992, 1994, 1995 Free Software Foundation, Inc.
//
// This file is free software; you can redistribute it and/or modify it
// under the terms of the GNU General Public License as published by the
// Free Software Foundation; either version 2, or (at your option) any
// later version.
//
// In addition to the permissions in the GNU General Public License, the
// Free Software Foundation gives you unlimited permission to link the
// compiled version of this file into combinations with other programs,
// and to distribute those combinations without any restriction coming
// from the use of this file.  (The General Public License restrictions
// do apply in other respects; for example, they cover modification of
// the file, and distribution when not linked into a combine
// executable.)
//
// This file is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
// General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; see the file COPYING.  If not, write to
// the Free Software Foundation, 59 Temple Place - Suite 330,
// Boston, MA 02111-1307, USA.  
//

#if defined(__GNUC__) || defined(__TINYC__)

#define W_TYPE_SIZE   32
#define BITS_PER_UNIT 8

typedef int Wtype;
typedef unsigned int UWtype;
typedef unsigned int USItype;
typedef long long DWtype;
typedef unsigned long long UDWtype;

struct DWstruct  {
  Wtype low, high;
};

typedef union {
  struct DWstruct s;
  DWtype ll;
} DWunion;

typedef long double XFtype;

#define WORD_SIZE (sizeof (Wtype) * BITS_PER_UNIT)
#define HIGH_WORD_COEFF (((UDWtype) 1) << WORD_SIZE)

// The following deals with IEEE single-precision numbers

#define EXCESS          126
#define SIGNBIT         0x80000000
#define HIDDEN          (1 << 23)
#define SIGN(fp)        ((fp) & SIGNBIT)
#define EXP(fp)         (((fp) >> 23) & 0xFF)
#define MANT(fp)        (((fp) & 0x7FFFFF) | HIDDEN)
#define PACK(s,e,m)     ((s) | ((e) << 23) | (m))

// The following deal with IEEE double-precision numbers

#define EXCESSD         1022
#define HIDDEND         (1 << 20)
#define EXPD(fp)        (((fp.l.upper) >> 20) & 0x7FF)
#define SIGND(fp)       ((fp.l.upper) & SIGNBIT)
#define MANTD(fp)       (((((fp.l.upper) & 0xFFFFF) | HIDDEND) << 10) | (fp.l.lower >> 22))
#define HIDDEND_LL      ((long long)1 << 52)
#define MANTD_LL(fp)    ((fp.ll & (HIDDEND_LL-1)) | HIDDEND_LL)
#define PACKD_LL(s,e,m) (((long long)((s)+((e)<<20))<<32)|(m))

// The following deal with x86 long double-precision numbers

#define EXCESSLD        16382
#define EXPLD(fp)       (fp.l.upper & 0x7fff)
#define SIGNLD(fp)      ((fp.l.upper) & 0x8000)

// Only for x86

union ldouble_long {
  long double ld;
  struct {
    unsigned long long lower;
    unsigned short upper;
  } l;
};

union double_long {
  double d;
  struct {
    unsigned long lower;
    long upper;
  } l;
  long long ll;
};

union float_long {
  float f;
  long l;
};

#if defined(__i386__)

#define sub_ddmmss(sh, sl, ah, al, bh, bl) \
  __asm__ ("subl %5,%1\n\tsbbl %3,%0"                                   \
           : "=r" ((USItype) (sh)),                                     \
             "=&r" ((USItype) (sl))                                     \
           : "0" ((USItype) (ah)),                                      \
             "g" ((USItype) (bh)),                                      \
             "1" ((USItype) (al)),                                      \
             "g" ((USItype) (bl)))

#define umul_ppmm(w1, w0, u, v) \
  __asm__ ("mull %3"                                                    \
           : "=a" ((USItype) (w0)),                                     \
             "=d" ((USItype) (w1))                                      \
           : "%0" ((USItype) (u)),                                      \
             "rm" ((USItype) (v)))

#define udiv_qrnnd(q, r, n1, n0, dv) \
  __asm__ ("divl %4"                                                    \
           : "=a" ((USItype) (q)),                                      \
             "=d" ((USItype) (r))                                       \
           : "0" ((USItype) (n0)),                                      \
             "1" ((USItype) (n1)),                                      \
             "rm" ((USItype) (dv)))

#define count_leading_zeros(count, x) \
  do {                                                                  \
    USItype __cbtmp;                                                    \
    __asm__ ("bsrl %1,%0"                                               \
             : "=r" (__cbtmp) : "rm" ((USItype) (x)));                  \
    (count) = __cbtmp ^ 31;                                             \
  } while (0)

#else

#error unsupported CPU type

#endif

// Most of this code is taken from libgcc2.c from gcc

static UDWtype __udivmoddi4(UDWtype n, UDWtype d, UDWtype *rp) {
  DWunion ww;
  DWunion nn, dd;
  DWunion rr;
  UWtype d0, d1, n0, n1, n2;
  UWtype q0, q1;
  UWtype b, bm;

  nn.ll = n;
  dd.ll = d;

  d0 = dd.s.low;
  d1 = dd.s.high;
  n0 = nn.s.low;
  n1 = nn.s.high;

#if !UDIV_NEEDS_NORMALIZATION
  if (d1 == 0) {
    if (d0 > n1) {
      // 0q = nn / 0D
      udiv_qrnnd(q0, n0, n1, n0, d0);
      q1 = 0;
      // Remainder in n0.
    } else {
      // qq = NN / 0d
      if (d0 == 0) d0 = 1 / d0; // Divide intentionally by zero
      udiv_qrnnd(q1, n1, 0, n1, d0);
      udiv_qrnnd(q0, n0, n1, n0, d0);
      // Remainder in n0.
    }

    if (rp != 0) {
      rr.s.low = n0;
      rr.s.high = 0;
      *rp = rr.ll;
    }
  }

#else // UDIV_NEEDS_NORMALIZATION

  if (d1 == 0) {
    if (d0 > n1) {
      // 0q = nn / 0D
      count_leading_zeros(bm, d0);
      if (bm != 0) {
        // Normalize, i.e. make the most significant bit of the denominator set.
        d0 = d0 << bm;
        n1 = (n1 << bm) | (n0 >> (W_TYPE_SIZE - bm));
        n0 = n0 << bm;
      }

      udiv_qrnnd (q0, n0, n1, n0, d0);
      q1 = 0;
      // Remainder in n0 >> bm.
    } else {
      // qq = NN / 0d
      if (d0 == 0) d0 = 1 / d0;       // Divide intentionally by zero.
      count_leading_zeros(bm, d0);
      if (bm == 0) {
        // From (n1 >= d0) /\ (the most significant bit of d0 is set),
        // conclude (the most significant bit of n1 is set) /\ (the
        // leading quotient digit q1 = 1).
        //
        // This special case is necessary, not an optimization.
        // (Shifts counts of W_TYPE_SIZE are undefined.)  */

        n1 -= d0;
        q1 = 1;
      } else {
        // Normalize
        b = W_TYPE_SIZE - bm;

        d0 = d0 << bm;
        n2 = n1 >> b;
        n1 = (n1 << bm) | (n0 >> b);
        n0 = n0 << bm;

        udiv_qrnnd(q1, n1, n2, n1, d0);
      }

      // n1 != d0...
      udiv_qrnnd(q0, n0, n1, n0, d0);

      // Remainder in n0 >> bm.
    }

    if (rp != 0) {
      rr.s.low = n0 >> bm;
      rr.s.high = 0;
      *rp = rr.ll;
    }
  }
#endif // UDIV_NEEDS_NORMALIZATION

  else {
    if (d1 > n1) {
      // 00 = nn / DD
      q0 = 0;
      q1 = 0;

      // Remainder in n1n0.
      if (rp != 0) {
        rr.s.low = n0;
        rr.s.high = n1;
        *rp = rr.ll;
      }
    } else {
      // 0q = NN / dd
      count_leading_zeros(bm, d1);
      if (bm == 0) {
        // From (n1 >= d1) /\ (the most significant bit of d1 is set),
        // conclude (the most significant bit of n1 is set) /\ (the
        // quotient digit q0 = 0 or 1).
        //
        // This special case is necessary, not an optimization.
        //
        // The condition on the next line takes advantage of that
        // n1 >= d1 (true due to program flow).

        if (n1 > d1 || n0 >= d0) {
          q0 = 1;
          sub_ddmmss(n1, n0, n1, n0, d1, d0);
        } else {
          q0 = 0;
        }

        q1 = 0;

        if (rp != 0) {
          rr.s.low = n0;
          rr.s.high = n1;
          *rp = rr.ll;
        }
      } else {
        UWtype m1, m0;
        // Normalize.
        b = W_TYPE_SIZE - bm;

        d1 = (d1 << bm) | (d0 >> b);
        d0 = d0 << bm;
        n2 = n1 >> b;
        n1 = (n1 << bm) | (n0 >> b);
        n0 = n0 << bm;

        udiv_qrnnd(q0, n1, n2, n1, d1);
        umul_ppmm(m1, m0, q0, d0);

        if (m1 > n1 || (m1 == n1 && m0 > n0)) {
          q0--;
          sub_ddmmss(m1, m0, m1, m0, d1, d0);
        }

        q1 = 0;

        // Remainder in (n1n0 - m1m0) >> bm.
        if (rp != 0) {
          sub_ddmmss(n1, n0, n1, n0, m1, m0);
          rr.s.low = (n1 << b) | (n0 >> bm);
          rr.s.high = n1 >> bm;
          *rp = rr.ll;
        }
      }
    }
  }

  ww.s.low = q0;
  ww.s.high = q1;
  return ww.ll;
}

#define __negdi2(a) (-(a))

long long __divdi3(long long u, long long v) {
  int c = 0;
  DWunion uu, vv;
  DWtype w;

  uu.ll = u;
  vv.ll = v;

  if (uu.s.high < 0) {
    c = ~c;
    uu.ll = __negdi2(uu.ll);
  }

  if (vv.s.high < 0) {
    c = ~c;
    vv.ll = __negdi2(vv.ll);
  }

  w = __udivmoddi4(uu.ll, vv.ll, (UDWtype *) 0);
  if (c) w = __negdi2(w);
  return w;
}

long long __moddi3(long long u, long long v) {
  int c = 0;
  DWunion uu, vv;
  DWtype w;

  uu.ll = u;
  vv.ll = v;

  if (uu.s.high < 0) {
    c = ~c;
    uu.ll = __negdi2(uu.ll);
  }

  if (vv.s.high < 0) vv.ll = __negdi2(vv.ll);
  __udivmoddi4 (uu.ll, vv.ll, (UDWtype *) &w);
  if (c) w = __negdi2(w);
  return w;
}

unsigned long long __udivdi3(unsigned long long u, unsigned long long v) {
  return __udivmoddi4(u, v, (UDWtype *) 0);
}

unsigned long long __umoddi3(unsigned long long u, unsigned long long v) {
  UDWtype w;
  
  __udivmoddi4(u, v, &w);
  return w;
}

long long __sardi3(long long a, int b) {
  DWunion u;

  u.ll = a;
  if (b >= 32) {
    u.s.low = u.s.high >> (b - 32);
    u.s.high = u.s.high >> 31;
  } else if (b != 0) {
    u.s.low = ((unsigned) u.s.low >> b) | (u.s.high << (32 - b));
    u.s.high = u.s.high >> b;
  }

  return u.ll;
}

unsigned long long __shrdi3(unsigned long long a, int b) {
  DWunion u;

  u.ll = a;
  if (b >= 32) {
    u.s.low = (unsigned) u.s.high >> (b - 32);
    u.s.high = 0;
  } else if (b != 0) {
    u.s.low = ((unsigned) u.s.low >> b) | (u.s.high << (32 - b));
    u.s.high = (unsigned) u.s.high >> b;
  }

  return u.ll;
}

long long __shldi3(long long a, int b) {
  DWunion u;

  u.ll = a;
  
  if (b >= 32) {
    u.s.high = (unsigned) u.s.low << (b - 32);
    u.s.low = 0;
  } else if (b != 0) {
    u.s.high = ((unsigned)u.s.high << b) | ((unsigned)u.s.low >> (32 - b));
    u.s.low = (unsigned) u.s.low << b;
  }

  return u.ll;
}

#if defined(__i386__)
// FPU control word for rounding to nearest mode
unsigned short __tcc_fpu_control = 0x137f;
// FPU control word for round to zero mode for int conversion
unsigned short __tcc_int_fpu_control = 0x137f | 0x0c00;
#endif

float __ulltof(unsigned long long a) {
  DWunion uu; 
  XFtype r;

  uu.ll = a;
  if (uu.s.high >= 0) {
    return (float) uu.ll;
  } else {
    r = (XFtype) uu.ll;
    r += 18446744073709551616.0;
    return (float) r;
  }
}

double __ulltod(unsigned long long a) {
  DWunion uu; 
  XFtype r;

  uu.ll = a;
  if (uu.s.high >= 0) {
    return (double) uu.ll;
  } else {
    r = (XFtype) uu.ll;
    r += 18446744073709551616.0;
    return (double) r;
  }
}

long double __ulltold(unsigned long long a) {
  DWunion uu; 
  XFtype r;

  uu.ll = a;
  if (uu.s.high >= 0) {
    return (long double) uu.ll;
  } else {
    r = (XFtype) uu.ll;
    r += 18446744073709551616.0;
    return (long double) r;
  }
}

unsigned long long __fixunssfdi(float a1) {
  register union float_long fl1;
  register int exp;
  register unsigned long l;

  fl1.f = a1;

  if (fl1.l == 0) return 0;

  exp = EXP (fl1.l) - EXCESS - 24;

  l = MANT(fl1.l);
  if (exp >= 41) {
    return (unsigned long long) -1;
  } else if (exp >= 0) {
    return (unsigned long long) l << exp;
  } else if (exp >= -23) {
    return l >> -exp;
  } else {
    return 0;
  }
}

unsigned long long __fixunsdfdi(double a1) {
  register union double_long dl1;
  register int exp;
  register unsigned long long l;

  dl1.d = a1;

  if (dl1.ll == 0) return 0;

  exp = EXPD (dl1) - EXCESSD - 53;

  l = MANTD_LL(dl1);

  if (exp >= 12) {
    return (unsigned long long) -1;
  } else if (exp >= 0) {
    return l << exp;
  } else if (exp >= -52) {
    return l >> -exp;
  } else {
    return 0;
  }
}

unsigned long long __fixunsxfdi(long double a1) {
  register union ldouble_long dl1;
  register int exp;
  register unsigned long long l;

  dl1.ld = a1;

  if (dl1.l.lower == 0 && dl1.l.upper == 0) return 0;

  exp = EXPLD (dl1) - EXCESSLD - 64;

  l = dl1.l.lower;

  if (exp > 0) {
    return (unsigned long long) -1;
  } else if (exp >= -63) {
    return l >> -exp;
  } else {
    return 0;
  }
}

#endif
